﻿using ArtMath.Core.Data;
using ArtMath.PointCollection.Helper;
using ArtMath.Setting;
using System;
using System.Linq;
using System.Collections.Generic;

namespace ArtMath.PointCollection
{
    [Serializable]
    public class PointCollectionsManager
    {
        public HashSet<PointCollectionBase> PointCollections { get; } = new HashSet<PointCollectionBase>();
        Dictionary<PointCollectionBase, PointCollectionBase> tailJoints, headJoints;
        public PointCollectionsManager(params PointCollectionBase[] pcs)
        {
            AddPointCollection(pcs);
        }
        public PointCollectionsManager(IEnumerable<PointCollectionBase> pcs)
        {
            AddPointCollection(pcs);
        }
        //public PointCollectionsManager(params PointCollectionsManager[] mgrs)//二义性警告！
        //{
        //    AddPointCollection(mgrs);
        //}
        public PointCollectionsManager(IEnumerable<PointCollectionsManager> mgrs)
        {
            AddPointCollection(mgrs);
        }
        public bool AddPointCollection(PointCollectionBase pc)
        {
            if (pc == Empty.Instance)
            {
                return false;
            }
            return PointCollections.Add(pc);
        }
        public bool AddPointCollection(IEnumerable<PointCollectionBase> pcs)
        {
            bool res = true;
            foreach (PointCollectionBase item in pcs)
            {
                if (!AddPointCollection(item))
                {
                    if (res) res = false;
                }
            }
            return res;
        }
        public bool AddPointCollection(PointCollectionsManager mgr)
        {
            return AddPointCollection(mgr.PointCollections);
        }
        public bool AddPointCollection(IEnumerable<PointCollectionsManager> mgrs)
        {
            bool res = true;
            foreach (PointCollectionsManager item in mgrs)
            {
                if (!AddPointCollection(item))
                {
                    if (res) res = false;
                }
            }
            return res;
        }
        public void ConnectHeadAndTail(PointCollectionBase tail, PointCollectionBase head)
        {
            if (tailJoints == null)
            {
                tailJoints = new Dictionary<PointCollectionBase, PointCollectionBase>();
            }
            tailJoints.Add(tail, head);
            if (headJoints == null)
            {
                headJoints = new Dictionary<PointCollectionBase, PointCollectionBase>();
            }
            headJoints.Add(head, tail);
        }
        public void ConnectHeads(PointCollectionBase head1, PointCollectionBase head2)
        {
            if (headJoints == null)
            {
                headJoints = new Dictionary<PointCollectionBase, PointCollectionBase>();
            }
            headJoints.Add(head1, head2);
            headJoints.Add(head2, head1);
        }
        public void ConnectTails(PointCollectionBase tail1, PointCollectionBase tail2)
        {
            if (tailJoints == null)
            {
                tailJoints = new Dictionary<PointCollectionBase, PointCollectionBase>();
            }
            tailJoints.Add(tail1, tail2);
            tailJoints.Add(tail2, tail1);
        }
        public PointCollectionsManager Intersect(PointCollectionBase pc)
        {
            return Intersect(new PointCollectionBase[] { pc });
        }
        public PointCollectionsManager Intersect(IEnumerable<PointCollectionBase> pcs)
        {
            PointCollectionsManager res = new PointCollectionsManager();
            if (PointCollections.Count > 0 && pcs != null)
            {
                foreach (PointCollectionBase item1 in PointCollections)
                {
                    foreach (PointCollectionBase item2 in pcs)
                    {
                        res.AddPointCollection(PointCollectionBase.Intersect(item1, item2));
                    }
                }
            }
            return res;
        }
        public bool IsUniversal
        {
            get
            {
                foreach (PointCollectionBase item in PointCollections)
                {
                    if (item is Universal)
                    {
                        return true;
                    }
                }
                return false;
            }
        }
        public bool IsEmptySet
        {
            get
            {
                if (PointCollections == null || PointCollections.Count == 0) return true;
                if (PointCollections.All((PointCollectionBase pc) => { return pc == Empty.Instance; }))
                {
                    PointCollections.Clear();
                    return true;
                }
                return false;
            }
        }
        public PointD? GetNearestPoint(PointD pt)
        {
            return PointCollections.GetNearestPoint(pt);
        }
        public bool InPointCollections(PointD pt)
        {
            return PointCollections.InPointCollections(pt);
        }
        public PointCollectionsManager Intersect(PointCollectionsManager mgr)
        {
            PointCollectionsManager res = Intersect(mgr.PointCollections);
            if (res != null)
            {
                if (IsUniversal)
                {
                    if (mgr.tailJoints != null)
                    {
                        res.tailJoints = new Dictionary<PointCollectionBase, PointCollectionBase>();
                        foreach (KeyValuePair<PointCollectionBase, PointCollectionBase> item in mgr.tailJoints)
                        {
                            res.tailJoints.Add(item.Key, item.Value);
                        }
                    }
                    if (mgr.headJoints != null)
                    {
                        res.headJoints = new Dictionary<PointCollectionBase, PointCollectionBase>();
                        foreach (KeyValuePair<PointCollectionBase, PointCollectionBase> item in mgr.headJoints)
                        {
                            res.headJoints.Add(item.Key, item.Value);
                        }
                    }
                }
                else if (mgr.IsUniversal)
                {
                    if (tailJoints != null)
                    {
                        res.tailJoints = new Dictionary<PointCollectionBase, PointCollectionBase>();
                        foreach (KeyValuePair<PointCollectionBase, PointCollectionBase> item in tailJoints)
                        {
                            res.tailJoints.Add(item.Key, item.Value);
                        }
                    }
                    if (headJoints != null)
                    {
                        res.headJoints = new Dictionary<PointCollectionBase, PointCollectionBase>();
                        foreach (KeyValuePair<PointCollectionBase, PointCollectionBase> item in headJoints)
                        {
                            res.headJoints.Add(item.Key, item.Value);
                        }
                    }
                }
            }
            return res;
        }
        
        public PointD SlideDestination(PointD currentLocation, double length, out bool reverse)
        {
            reverse = false;
            PointCollectionBase currentPC = PointCollections.OnWhichPointCollection(currentLocation);
            if (currentPC == null)
            {
                return currentLocation;
            }
            double excess;
            while ((excess = currentPC.GetSlideExcess(currentLocation, length)) > 0)
            {
                if (Settings.Instance.AnimateOnOneCurve)
                {
                    CurveDescribable cd = currentPC as CurveDescribable;
                    if (cd != null && cd.CurrentCurve != null)
                    {
                        reverse = !reverse;
                        if (length > 0)
                        {
                            currentLocation = cd.CurrentCurve[cd.CurrentCurve.Count - 1];
                            length = -excess;
                        }
                        else
                        {
                            currentLocation = cd.CurrentCurve[0];
                            length = excess;
                        }
                        continue;
                    }
                }
                PointCollectionBase tmp;
                if (length > 0)
                {
                    if (tailJoints != null && tailJoints.TryGetValue(currentPC, out tmp))
                    {
                        PointD? head = tmp.HeadPoint;
                        if (head.HasValue)
                        {
                            currentPC = tmp;
                            currentLocation = head.Value;
                            length = excess;
                            continue;
                        }
                    }
                }
                else
                {
                    if (headJoints != null && headJoints.TryGetValue(currentPC, out tmp))
                    {
                        PointD? tail = tmp.TailPoint;
                        if (tail.HasValue)
                        {
                            currentPC = tmp;
                            currentLocation = tail.Value;
                            length = -excess;
                            continue;
                        }
                    }
                }
                reverse = !reverse;
                if (length > 0)
                {
                    currentLocation = currentPC.TailPoint.Value;
                    length = -excess;
                }
                else
                {
                    currentLocation = currentPC.HeadPoint.Value;
                    length = excess;
                }
            }
            return currentPC.SlideDestination(currentLocation, length, out bool waste);
        }
    }
}
